package org.gwtEss.menu;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.gwtEss.other.HasWidgetResizerRequiredHandlers;
import org.gwtEss.other.WidgetResizer;
import org.gwtEss.table.ColWidget;

import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;

public abstract class Menu extends PopupPanel implements HasPopupMenu, HasWidgetResizerRequiredHandlers{

	@Override
	public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
		return addDomHandler(handler, MouseDownEvent.getType());		
	}

	@Override
	public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
		return addDomHandler(handler, MouseMoveEvent.getType());
	}
	private MenuTable tab;
	private boolean autoFit = true; // Also change ManuTable.autoFit if this
									// variable is changed.
	private List<? extends MenuItemBase> data;

	public Menu() {
		setAutoHideEnabled(true);
		setWidget(tab = new MenuTable(){
			
			@Override
			public void onRowCellClick(MenuItemBase data, ColWidget colWidget) {
				if (data instanceof HasSelectable && ((HasSelectable)data).onSelect())
					Menu.this.onMenuItemSelect();
				super.onRowCellClick(data, colWidget);
			}

			@Override
			public void onAfterRowHilite(MenuItemBase data) {
//				if (hilitedMenuItem != null && hilitedMenuItem == data)
//					return; 
//				hilitedMenuItem = data; 
//				HasMenuItem menuItem = (HasMenuItem)data;
//				List<? extends MenuItemBase> subMenuItems = menuItem.getSubMenu();
				if (!(data instanceof HasMenuItemSubMenu))
					return; 
				SubMenuItemBase subMenuItems = ((HasMenuItemSubMenu)data).getSubMenu();
				if (subMenuItems != null /*&& (hilitedMenuItemRow == null || hilitedMenuItemRow != menuItem) *//*&& subMenuItems.size() > 0*/){
					setSubMenu(subMenuItems, super.getRowAbsoluteTop(data));
					/*
					HasPopupMenu popupMenu = null; 
					int subMenuTop = super.getRowAbsoluteTop(data);
					if (subMenuItems instanceof HasSubMenuItems){
						if (Menu.this.subMenu == null)
							Menu.this.subMenu = new SubMenuBase();	
						Menu.this.subMenu.top = subMenuTop;
						Menu.this.subMenu.left = super.getAbsoluteLeft() + super.getOffsetWidth();
						Menu.this.subMenu.setData((HasSubMenuItems)subMenuItems);
						popupMenu = Menu.this.subMenu; 
					}else if (subMenuItems instanceof HasSubMenuItemWidget){
						if (Menu.this.subMenuWidget == null)
							Menu.this.subMenuWidget = new SubMenuBase();
						Menu.this.subMenuWidget.top = subMenuTop;
						Menu.this.subMenuWidget.left = super.getAbsoluteLeft() + super.getOffsetWidth();
						Menu.this.subMenuWidget.setWidget(((HasSubMenuItemWidget)subMenuItems).createWidget(new HasSelectableCallback() {
							
							@Override
							public void onSelect(boolean closeMenu) {
								if (closeMenu)
									Menu.this.onMenuItemSelect();
							}
						}));
						popupMenu = Menu.this.subMenuWidget;
					}
					
//					subMenu.setData(subMenuItems);
					if (popupMenu != null)
					popupMenu.show();
				*/}
//				hilitedMenuItemRow = menuItem;
//				super.onAfterRowHilite(data);
			}
			
			@Override
			public void onAfterRowUnHilite(MenuItemBase data) {
				closeSubMenu();
				super.onAfterRowUnHilite(data);
			}
			
		});
		
		addCloseHandler(new CloseHandler<PopupPanel>() {

			@Override
			public void onClose(CloseEvent<PopupPanel> event) {
				/*tab.*/closeSubMenu();
			}
		});
		new WidgetResizer((Widget)this);
	}
	public void closeSubMenu() {
		if (subMenus == null) return;
		Iterator<SubMenuItemBase> keysItt = subMenus.keySet().iterator();
		while (keysItt.hasNext()) {
			SubMenuItemBase subMenuItem = (SubMenuItemBase) keysItt.next();
			subMenus.get(subMenuItem).hide();
		}
		/*if (subMenu != null){
			subMenu.hide();
//			subMenu.closeSubMenu();
		}
		if (subMenuWidget != null)
			subMenuWidget.hide();*/
	}
	private class SubMenuBase extends Menu{
		int top, left;
		public SubMenuBase() {
			super();
			setAutoFit(autoFit);
			super.addAutoHidePartner(Menu.this.getElement());
		}
		@Override
		public int getMenuLeft() {
			return left;
		}

		@Override
		public int getMenuTop() {
			return top;
		}
		@Override
		protected void onMenuItemSelect() {
			Menu.this.onMenuItemSelect();
			super.onMenuItemSelect();
		}
	}
//	private SubMenuBase subMenu, subMenuWidget;
	private Map<SubMenuItemBase, SubMenuBase> subMenus; 
	private void setSubMenu(SubMenuItemBase subMenuItems, int subMenuTop){
		if (subMenus == null)
			subMenus = new HashMap<SubMenuItemBase, SubMenuBase>();
		SubMenuBase subMenu = subMenus.get(subMenuItems);
		if (subMenu == null)
			subMenus.put(subMenuItems, subMenu = new SubMenuBase());
		subMenu.top = subMenuTop;
		subMenu.left = super.getAbsoluteLeft() + super.getOffsetWidth();
		if (subMenuItems instanceof HasSubMenuItems){
			subMenu.setData((HasSubMenuItems)subMenuItems);
		}else if (subMenuItems instanceof HasSubMenuItemWidget){
			subMenu.setWidget(((HasSubMenuItemWidget)subMenuItems).createWidget(new HasSelectableCallback() {
				
				@Override
				public void onSelect(boolean closeMenu) {
					if (closeMenu)
						Menu.this.onMenuItemSelect();
				}
			}));
//			popupMenu = Menu.this.subMenuWidget;
		}
		subMenu.show();
	}
	private int getAutoFitLeft() {
		int clientWidth = Window.getClientWidth();
		int scrollLeft = Window.getScrollLeft();
		int popupWidth = getOffsetWidth();
		int popupLeft = getPopupLeft();
		int popupBottom = popupLeft + popupWidth;
		int visibleWidth = clientWidth - scrollLeft;
		boolean popupBiggerThanVisibleWidth = popupWidth > visibleWidth;
		if (popupBiggerThanVisibleWidth)
			return scrollLeft;
		// setPopupPosition(scrollLeft, getPopupTop());//TODO: Auto move popup
		// up/down as the user scrolls!
		else {
			if (popupBottom > clientWidth)
				return clientWidth + scrollLeft - popupWidth;
		}
		return popupLeft;
	}

	private int getAutoFitTop() {
		int clientHeight = Window.getClientHeight();
		int scrollTop = Window.getScrollTop();
		int popupHeight = getOffsetHeight();
		int popupTop = getPopupTop();
		int popupBottom = popupTop + popupHeight;
		int visibleHeight = clientHeight - scrollTop;
		boolean popupBiggerThanVisibleHeight = popupHeight > visibleHeight;
		if (popupBiggerThanVisibleHeight)
			return scrollTop;
		else {
			if (popupBottom > clientHeight)
				return clientHeight + scrollTop - popupHeight;
		}
		return popupTop;
	}

	private void doAutoFit() {
		setPopupPosition(getAutoFitLeft(), getAutoFitTop());
	}

	public void show() {
		System.out.println("rendering");
		if (getWidget() instanceof MenuTable){
			
		}
		tab.removeRowColHilite();
		int menuTop = getMenuTop();
		super.setPopupPosition(getMenuLeft(), menuTop);
		tab.renderTab(data);
		super.show();
		if (autoFit)
			doAutoFit();
	}
	
	public boolean isAutoFit() {
		return autoFit;
	}

	public void setAutoFit(boolean autoFit) {
		this.autoFit = autoFit;
		/*if (subMenu != null)
		subMenu.setAutoFit(autoFit);*/
		
		if (subMenus != null){
			Iterator<SubMenuItemBase> keyItt = subMenus.keySet().iterator();
			while (keyItt.hasNext()) {
				SubMenuItemBase subMenuItem = (SubMenuItemBase) keyItt
						.next();
				subMenus.get(subMenuItem).setAutoFit(autoFit);
//				((Menu)subMenuItem).setAutoFit(autoFit);
			}
		}
	}

	public List<? extends MenuItemBase> getData() {
		return data;
	}

	public void setData(List<? extends MenuItemBase> data) {
		this.data = data;
	}
	protected void onMenuItemSelect(){
		hide();
	}
}
